/*
 * Copyright (C) 2001 Clarkware Consulting, Inc.
 * All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *  3. Neither the name of Clarkware Consulting, Inc. nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without prior written permission. For written
 *     permission, please contact clarkware@clarkware.com.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * CLARKWARE CONSULTING OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN  ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package daikon.util;

import java.io.*;
import java.net.URL;
import java.util.StringTokenizer;

// PROBLEM:  The "java" binary places rt.jar at the front of the
// bootclasspath, and this program will find a class there even if the
// class appears at the beginning of the real classpath.

/**
 * <code>JWhich</code> is a utility that takes a Java class name
 * and displays the absolute pathname of the class file that would
 * be loaded first by the class loader, as prescribed by the
 * class path.
 * <p>
 * <code>JWhich</code> also validates the class path and reports
 * any non-existent or invalid class path entries.
 * <p>
 * Usage is similar to the UNIX <code>which</code> command.
 * <p>
 * Example uses:
 * <p>
 * <blockquote>
 *    To find the absolute pathname of <code>MyClass.class</code>
 *    not in a package:
 *    <pre>java JWhich MyClass</pre>
 *
 *    To find the absolute pathname of <code>MyClass.class</code>
 *    in the <code>my.package</code> package:
 *    <pre>java JWhich my.package.MyClass</pre>
 * </blockquote>
 *
 * @author <a href="mailto:mike@clarkware.com">Mike Clark</a>
 * @author <a href="http://www.clarkware.com">Clarkware Consulting, Inc.</a>
 */

public class JWhich {

  private static String CLASSPATH;

  /**
   * Prints the absolute pathname of the class file
   * containing the specified class name, as prescribed
   * by the class path.
   *
   * @param className Name of the class.
   */
  public static void which(String className) {

    URL classUrl = findClass(className);

    if (classUrl == null) {
      System.out.println("\nClass '" + className +
        "' not found.");
    } else {
      System.out.println("\nClass '" + className +
        "' found in \n'" + classUrl.getFile() + "'");
    }

    validate();

    printClasspath();
  }

  /**
   * Returns the URL of the resource denoted by the specified
   * class name, as prescribed by the class path.
   *
   * @param className Name of the class.
   * @return Class URL, or null of the class was not found.
   */
  public static /*@Nullable*/ URL findClass(final String className) {
    return JWhich.class.getResource(asResourceName(className));
  }

  protected static String asResourceName(String resource) {
    if (!resource.startsWith("/")) {
      resource = "/" + resource;
    }
    resource = resource.replace('.', '/');
    resource = resource + ".class";
    return resource;
  }

  /**
   * Validates the class path and reports any non-existent
   * or invalid class path entries.
   * <p>
   * Valid class path entries include directories, <code>.zip</code>
   * files, and <code>.jar</code> files.
   */
  public static void validate() {

    StringTokenizer tokenizer =
      new StringTokenizer(getClasspath(), File.pathSeparator);

    while (tokenizer.hasMoreTokens()) {
      String element = tokenizer.nextToken();
      File f = new File(element);

      if (!f.exists()) {
        System.out.println("\nClasspath element '" +
          element + "' " + "does not exist.");
      } else if ( (!f.isDirectory()) &&
            (!element.toLowerCase().endsWith(".jar")) &&
            (!element.toLowerCase().endsWith(".zip")) ) {

        System.out.println("\nClasspath element '" +
          element + "' " +
          "is not a directory, .jar file, or .zip file.");

      }
    }
  }

  public static void printClasspath() {

    System.out.println("\nClasspath:");
    StringTokenizer tokenizer =
      new StringTokenizer(getClasspath(), File.pathSeparator);
    while (tokenizer.hasMoreTokens()) {
      System.out.println(tokenizer.nextToken());
    }
  }

  public static void setClasspath(String classpath) {
    CLASSPATH = classpath;
  }

  protected static String getClasspath() {
    if (CLASSPATH == null) {
      @SuppressWarnings("nullness") // java.class.path property always exists
      /*@NonNull*/ String classpath = System.getProperty("java.class.path");
      setClasspath(classpath);
    }

    return CLASSPATH;
  }

  private static void instanceMain(String[] args) {

    if (args.length == 0) {
      printUsage();
    }

    for (int cmdIndex = 0; cmdIndex < args.length; cmdIndex++) {

      String cmd = args[cmdIndex];

      if ("-help".equals(cmd)) {
        printUsage();
      } else {
        which(cmd);
      }
    }
  }

  private static void printUsage() {

    System.out.println("\nSyntax: java JWhich [options] className");
    System.out.println("");
    System.out.println("where options include:");
    System.out.println("");
    System.out.println("\t-help     Prints usage information.");
    System.out.println("");
    System.out.println("Examples:");
    System.out.println("\tjava JWhich MyClass");
    System.out.println("\tjava JWhich my.package.MyClass");
    System.exit(0);
  }

  public static void main(String[] args) {
    instanceMain(args);
  }
}

/*
Local Variables:
tab-width: 2
End:
*/
